﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Go;

namespace fhs
{
    class DataConfuse
    {
        private const int N = 624;
        private const int M = 397;
        private const uint MatrixA = 0x9908b0df;
        private const uint UpperMask = 0x80000000;
        private const uint LowerMask = 0x7fffffff;
        private const uint TemperingMaskB = 0x9d2c5680;
        private const uint TemperingMaskC = 0xefc60000;
        private const double FiftyThreeBitsOf1s = 9007199254740991.0;
        private const double Inverse53BitsOf1s = 1.0 / FiftyThreeBitsOf1s;
        private const double OnePlus53BitsOf1s = FiftyThreeBitsOf1s + 1;
        private const double InverseOnePlus53BitsOf1s = 1.0 / OnePlus53BitsOf1s;

        private static readonly uint[] _mag01 = { 0x0, MatrixA };
        private readonly uint[] _mt = new uint[N];
        private short _mti;

        public DataConfuse()
        {
        }

        public Task<uint> NextUInt32()
        {
            return GenerateUInt32();
        }

        public async Task<uint> NextUInt32(uint maxValue)
        {
            return (uint)(await GenerateUInt32() / ((double)uint.MaxValue / maxValue));
        }

        public async Task<uint> NextUInt32(uint minValue, uint maxValue)
        {
            if (minValue >= maxValue)
            {
                throw new ArgumentOutOfRangeException();
            }
            return (uint)(await GenerateUInt32() / ((double)uint.MaxValue / (maxValue - minValue)) + minValue);
        }

        public Task<int> Next()
        {
            return Next(int.MaxValue);
        }

        public async Task<int> Next(int maxValue)
        {
            if (maxValue <= 1)
            {
                if (maxValue < 0)
                {
                    throw new ArgumentOutOfRangeException();
                }
                return 0;
            }
            return (int)(await NextDouble() * maxValue);
        }

        public async Task<int> Next(int minValue, int maxValue)
        {
            if (maxValue <= minValue)
            {
                throw new ArgumentOutOfRangeException();
            }
            if (maxValue == minValue)
            {
                return minValue;
            }
            return await Next(maxValue - minValue) + minValue;
        }

        public async Task NextBytes(byte[] buffer)
        {
            await generator.non_async();
            if (buffer == null)
            {
                await generator.non_async();
                throw new ArgumentNullException();
            }
            await generator.non_async();
            int bufLen = buffer.Length;
            await generator.non_async();
            for (int idx = 0; idx < bufLen; ++idx)
            {
                buffer[idx] = (byte)await Next(256);
            }
            await generator.non_async();
        }

        public async Task Confuse(byte[] buffer, int offset, int length)
        {
            await generator.non_async();
            if (buffer == null)
            {
                await generator.non_async();
                throw new ArgumentNullException();
            }
            await generator.non_async();
            int bufLen = length;
            await generator.non_async();
            for (int idx = 0; idx < bufLen; ++idx)
            {
                buffer[offset + idx] ^= (byte)await Next(256);
            }
            await generator.non_async();
        }

        public Task<double> NextDouble()
        {
            return compute53BitRandom(0, InverseOnePlus53BitsOf1s);
        }

        public Task<double> NextDouble(bool includeOne)
        {
            return includeOne ? compute53BitRandom(0, Inverse53BitsOf1s) : NextDouble();
        }

        public Task<double> NextDoublePositive()
        {
            return compute53BitRandom(0.5, Inverse53BitsOf1s);
        }

        private async Task<uint> GenerateUInt32()
        {
            await generator.non_async();
            uint y;
            await generator.non_async();
            if (_mti >= N)
            {
                await generator.non_async();
                short kk = 0;
                await generator.non_async();
                for (; kk < N - M; ++kk)
                {
                    await generator.non_async();
                    y = (_mt[kk] & UpperMask) | (_mt[kk + 1] & LowerMask);
                    await generator.non_async();
                    _mt[kk] = _mt[kk + M] ^ (y >> 1) ^ _mag01[y & 0x1];
                }
                await generator.non_async();
                for (; kk < N - 1; ++kk)
                {
                    await generator.non_async();
                    y = (_mt[kk] & UpperMask) | (_mt[kk + 1] & LowerMask);
                    await generator.non_async();
                    _mt[kk] = _mt[kk + (M - N)] ^ (y >> 1) ^ _mag01[y & 0x1];
                }
                await generator.non_async();
                y = (_mt[N - 1] & UpperMask) | (_mt[0] & LowerMask);
                await generator.non_async();
                _mt[N - 1] = _mt[M - 1] ^ (y >> 1) ^ _mag01[y & 0x1];
                await generator.non_async();
                _mti = 0;
                await generator.non_async();
            }
            await generator.non_async();
            y = _mt[_mti++];
            await generator.non_async();
            y ^= temperingShiftU(y);
            await generator.non_async();
            y ^= temperingShiftS(y) & TemperingMaskB;
            await generator.non_async();
            y ^= temperingShiftT(y) & TemperingMaskC;
            await generator.non_async();
            y ^= temperingShiftL(y);
            await generator.non_async();
            return y;
        }

        private static uint temperingShiftU(uint y)
        {
            return (y >> 11);
        }

        private static uint temperingShiftS(uint y)
        {
            return (y << 7);
        }

        private static uint temperingShiftT(uint y)
        {
            return (y << 15);
        }

        private static uint temperingShiftL(uint y)
        {
            return (y >> 18);
        }

        public async Task Init(uint seed)
        {
            await generator.non_async();
            _mt[0] = seed & 0xffffffffU;
            await generator.non_async();
            for (_mti = 1; _mti < N; _mti++)
            {
                await generator.non_async();
                _mt[_mti] = (uint)(1812433253U * (_mt[_mti - 1] ^ (_mt[_mti - 1] >> 30)) + _mti);
                await generator.non_async();
                _mt[_mti] &= 0xffffffffU;
                await generator.non_async();
            }
            await generator.non_async();
        }

        public async Task Init(uint[] key)
        {
            await generator.non_async();
            int i, j, k;
            await generator.non_async();
            await Init(19650218U);
            await generator.non_async();
            int keyLength = key.Length;
            await generator.non_async();
            i = 1; j = 0;
            await generator.non_async();
            k = (N > keyLength ? N : keyLength);
            await generator.non_async();
            for (; k > 0; k--)
            {
                await generator.non_async();
                _mt[i] = (uint)((_mt[i] ^ ((_mt[i - 1] ^ (_mt[i - 1] >> 30)) * 1664525U)) + key[j] + j);
                await generator.non_async();
                _mt[i] &= 0xffffffffU;
                await generator.non_async();
                i++;
                await generator.non_async();
                j++;
                await generator.non_async();
                if (i >= N)
                {
                    await generator.non_async();
                    _mt[0] = _mt[N - 1];
                    await generator.non_async();
                    i = 1;
                    await generator.non_async();
                }
                await generator.non_async();
                if (j >= keyLength)
                {
                    await generator.non_async();
                    j = 0;
                    await generator.non_async();
                }
                await generator.non_async();
            }
            for (k = N - 1; k > 0; k--)
            {
                await generator.non_async();
                _mt[i] = (uint)((_mt[i] ^ ((_mt[i - 1] ^ (_mt[i - 1] >> 30)) * 1566083941U)) - i);
                await generator.non_async();
                _mt[i] &= 0xffffffffU;
                await generator.non_async();
                i++;
                await generator.non_async();
                if (i < N)
                {
                    await generator.non_async();
                    continue;
                }
                await generator.non_async();
                _mt[0] = _mt[N - 1]; i = 1;
                await generator.non_async();
            }
            await generator.non_async();
            _mt[0] = 0x80000000U;
            await generator.non_async();
        }

        private async Task<double> compute53BitRandom(double translate, double scale)
        {
            await generator.non_async();
            ulong a = (ulong)await GenerateUInt32() >> 5;
            await generator.non_async();
            ulong b = (ulong)await GenerateUInt32() >> 6;
            await generator.non_async();
            return ((a * 67108864.0 + b) + translate) * scale;
        }
    }
}
